#ifndef vector_h__
#define vector_h__
#include "coremath.h"

namespace gecko
{

// Templated 3D vector class.
template<typename T>
class VectorT
{
public:
	T x, y, z;

	// constructors
	VectorT();
	VectorT(T x, T y, T z);
	VectorT(const VectorT &_v);

	// operators	
	void    operator =  (const VectorT &_v);
	VectorT& operator += (const VectorT &_v);
	void    operator -= (const VectorT &_v);
	VectorT  operator +  (const VectorT &v1) const;
	VectorT  operator -  (const VectorT &v1) const;
	VectorT  operator *  (T n) const;
	void    operator *= (T n);
	void    operator /= (T n);
	VectorT  operator -  () const; // unary minus (read only)
	VectorT  operator /  (T b);
	T   operator *  (const VectorT &v1) const; // dot product
	VectorT  operator %  (const VectorT &v1) const ;// cross product
	bool    operator == (const VectorT &v1) const;
	bool    operator != (const VectorT &v1) const;
	bool    IsZero() const;

	// other operations
	T   Magnitude() const;
	T   MagnitudeSq() const;
	void	Normalise();
	void	Normalise(T* len);
	void    Invert();
	void    Zero();
	void    Randomise();
	void	Clamp(float length);
};

template < typename T >
inline VectorT<T>::VectorT() 
{ 
	x = 0.0f; 
	y = 0.0f; 
	z = 0.0f; 
}

template < typename T >
inline VectorT<T>::VectorT(T _x, T _y, T _z) 
{ 
	x = _x; 
	y = _y; 
	z = _z; 
}

template < typename T >
inline VectorT<T>::VectorT(const VectorT<T> &_v) 
{ 
	x = _v.x; 
	y = _v.y; 
	z = _v.z; 
}

template < typename T >
inline void VectorT<T>::operator =(const VectorT<T> &_v) 
{ 
	x = _v.x; 
	y = _v.y; 
	z = _v.z; 
}

template < typename T >
inline VectorT<T>& VectorT<T>::operator +=(const VectorT<T> &_v) 
{ 
	x += _v.x; 
	y += _v.y; 
	z += _v.z; 

	return *this;
}

template < typename T >
inline void VectorT<T>::operator -=(const VectorT<T> &_v) 
{ 
	x -= _v.x; 
	y -= _v.y; 
	z -= _v.z; 
}

template < typename T >
inline VectorT<T> VectorT<T>::operator +(const VectorT<T> &v1) const 
{ 
	return VectorT<T>(x + v1.x, y + v1.y, z + v1.z); 
}

template < typename T >
inline VectorT<T> VectorT<T>::operator -(const VectorT<T> &v1) const 
{ 
	return VectorT<T>(x - v1.x, y - v1.y, z - v1.z); 
}

template < typename T >
inline VectorT<T> VectorT<T>::operator *(T n) const
{ 
	return VectorT<T>(x * n, y * n, z * n); 
}

template < typename T >
inline void VectorT<T>::operator *=(T n)
{ 
	x *= n;
	y *= n;
	z *= n;
}

template < typename T >
inline void VectorT<T>::operator /=(T n)
{ 
	x /= n;
	y /= n;
	z /= n;
}

template < typename T >
inline VectorT<T> VectorT<T>::operator -() const // unary minus (read only)
{ 
	return VectorT<T>(-x, -y, -z); 
}

template < typename T >
inline VectorT<T> VectorT<T>::operator /(T b)
{
	return VectorT<T>(x / b, y / b, z / b);
}

template < typename T >
inline T VectorT<T>::operator *(const VectorT<T> &v1) const // dot product
{ 
	return ( x * v1.x + 
		y * v1.y + 
		z * v1.z );
}

template < typename T >
inline VectorT<T> VectorT<T>::operator %(const VectorT<T> &v1) const // cross product
{
	VectorT<T> cross;
	cross.x = y * v1.z - z * v1.y;
	cross.y = z * v1.x - x * v1.z;
	cross.z = x * v1.y - y * v1.x;
	return cross;
}

template < typename T >
inline bool VectorT<T>::operator ==(const VectorT<T> &v1) const
{
	return ( (x == v1.x && y == v1.y && z == v1.z) ? true:false );
}

template < typename T >
inline bool VectorT<T>::operator !=(const VectorT<T> &v1) const
{
	return ( (x == v1.x && y == v1.y && z == v1.z) ? false:true );
}

template < typename T >
inline T VectorT<T>::Magnitude() const 
{
	return (T)sqrt(x * x + y * y + z * z); 
}

template < typename T >
inline T VectorT<T>::MagnitudeSq() const 
{
	return (x * x + y * y + z * z); 
}

template < typename T >
inline void VectorT<T>::Normalise()
{
	T len = Magnitude();
	if ( len == 0.0f )
		return;

	x /= len;
	y /= len;
	z /= len;
}

template < typename T >
inline void VectorT<T>::Normalise(T* len)
{
	*len = Magnitude();
	if ( *len == 0.0f )
		return;

	x /= *len;
	y /= *len;
	z /= *len;
}

template < typename T >
inline void VectorT<T>::Invert()
{
	x = -x;
	y = -y;
	z = -z;
}

template < typename T >
inline void VectorT<T>::Zero()
{
	x = 0.0f;
	y = 0.0f;
	z = 0.0f;
}

template < typename T >
inline void VectorT<T>::Randomise()
{
	x = Rand2<T>();
	y = Rand2<T>();
	z = Rand2<T>();
}

template < typename T >
inline VectorT<T> operator *(T n, VectorT<T> &v) 
{ 
	return VectorT<T>(v.x * n, v.y * n, v.z * n); 
}

template < typename T >
inline bool VectorT<T>::IsZero() const
{
	if ( (x == 0.0f) && (y == 0.0f) && (z == 0.0f) )
		return true;
	return false;
}

template < typename T >
inline void VectorT<T>::Clamp(float length)
{
	// get length squared
	float magSq = MagnitudeSq();

	// clamp if length is greater
	if (magSq > length * length)
	{
		// normalise
		Normalise();

		// make length
		x *= length;
		y *= length;
		z *= length;
	}
}

typedef VectorT<float>	Vector3f;
typedef VectorT<double> Vector3d;
	
}

#endif // vector_h__
